Can DXL functions be aliased?

The DXL reference manual states something like:

<Type>& <AliasVarName> = <ActualItemToAlias>;

Is supported. There is certainly no problem with use of this feature for simple variables.

I tried to see if could be used on functions. The alias association compiles, but is not usable.

Perhaps I have the syntax wrong.

Divide and conquer?:

I know if a function is to be passed to another function, this syntax works:
 

void passMeAFunction(string f(string)) {
}


The example above takes as a parameter a function that takes a string parameter that returns a string.

I've also seen (for example) returning a function that takes a string parameter that returns a string is written as:

 

 

string returnStringFuncString() (string){
}



For example:



 

 

 

string titleCaseStr(string str) {
    string retVal = "";
    int len = length(str);
    if (len > 0) {
        retVal = (upper("" str[0] "")) str[1:(len-1)];
    }
    return retVal;
}
 
string returnAStringProcessingFunction()(string) {
    return titleCaseStr;
}
 
void functionFun() {
    string str = "ABCD";
 
    string string1 = lower(str);
    print "string1 = " string1 "\n";
 
    print "Titlecase aaa is " returnAStringProcessingFunction()("aaaa") "\n";
}
 
functionFun(); // call it



THE ALIAS ISSUE WITH FUNCTIONS:
The below compiles but if the last line is un-commented it will bomb (why?):



 

 

void ACTUAL_printThis(string p1) {
    print("p1 = " p1 "\n");
}
 
void testAliases() {
   //-------------------------------------
   // This works (no suprise)
   //-------------------------------------
   ACTUAL_printThis("Parameter 1");
 
   //---------------------------------------------------------------------------
   // The below appears to conform to the syntax of an alias
   //     (e.g., <Type>& <AliasVarName> = <ActualItemToAlias>;
   // It compiles but does not work
   //---------------------------------------------------------------------------
 
   // Left is (desired) "alias to void function that takes a string parameter"
   void& printThis()(string) = ACTUAL_printThis; // compiles
 
   // printThis("Parameter ONE"); // COMPILER: incorrect use of identifier (printThis)
}
 
testAliases(); // Call it



Since DOORS DXL essentially has one big namespace and we can't use module prefixes to indicate where a function exists, sometimes I use a prefix to indicate it's home. I was hoping I could use function aliases to shorten some names locally if desired.

Am I off on the syntax or is aliasing functions impossible? Anyone had success with this?

Thanks.

OUTPUT:
p1 = Parameter 1
string1 = abcd
Titlecase aaa is Aaaa



 


SystemAdmin - Thu Jun 24 15:57:03 EDT 2010

Re: Can DXL functions be aliased?
Mathias Mamsch - Fri Jun 25 17:07:55 EDT 2010

Ok first if you declare void ACTUAL_printThis(string) you cannot set void& printThis()(string) = ACTUAL_printThis. The declaration types don't match since ACTUAL_printThis returns void not string ... That is why you get the compile error.

I tried using function reference variables but it does not seem to work. The DXL parser seems to recognize those as forward declarations.

The only way I know for function pointers is to just store them to an integer. To dereference and call them I know only the way of declaring a "proxy" function which takes the function pointers and the parameters.
 

int test(int a) {
   print "Hello " a "\n"
   return a + 1
}
 
 
int dereference( int func(int), int par ) { 
   return func(par) 
}
 
print test 4
 
int x = (addr_ test)
 
print dereference (addr_ x, 4)

 


Using the pseudo-object oriented approach with struct {} you can use this mechanism to store callback pointers in the object or function pointers. This should be sufficient for most applications.

Regards, Mathias

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

Re: Can DXL functions be aliased?
SystemAdmin - Fri Jun 25 18:40:35 EDT 2010

Mathias Mamsch - Fri Jun 25 17:07:55 EDT 2010

Ok first if you declare void ACTUAL_printThis(string) you cannot set void& printThis()(string) = ACTUAL_printThis. The declaration types don't match since ACTUAL_printThis returns void not string ... That is why you get the compile error.

I tried using function reference variables but it does not seem to work. The DXL parser seems to recognize those as forward declarations.

The only way I know for function pointers is to just store them to an integer. To dereference and call them I know only the way of declaring a "proxy" function which takes the function pointers and the parameters.
 

int test(int a) {
   print "Hello " a "\n"
   return a + 1
}
 
 
int dereference( int func(int), int par ) { 
   return func(par) 
}
 
print test 4
 
int x = (addr_ test)
 
print dereference (addr_ x, 4)

 


Using the pseudo-object oriented approach with struct {} you can use this mechanism to store callback pointers in the object or function pointers. This should be sufficient for most applications.

Regards, Mathias

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

Thanks for the reply.

I tried every syntax variation I could think of. I got those unresolved forward function messages as well. The closest I got was to return a function with the proper return type and signature. That works, but it has to be called like:
 

(functionName())(P1, P2, ...); // ugly

 


I was hoping to use your "New Types" with the struct NewTypeName {}; wiring

but couple it with:

 

 

 

::.(NewTypeName left, aliasedFunction) to do: 
 
instanceOfNewType.memberFunction(P1, P2, ...);
 
 
// as an alternate to:
 
memberFunction(instanceOfNewType, P1, P2, ...);


With my method of returning functions from functions I may be able to do it, but it may require something even uglier to evaluate properly like:

 

 

 

 

(instanceOfNewType.memberFunction())(P1, P2, ...);


This could give the "appearance" of member functions like C++ or Java (or even Object oriented JavaScript).

Without the means aliasing functions in a reasonably easy way such that DXL understands both memberFunction()'s return type AND signature, this will never work.

Like Clint Eastwood as Dirty Harry says "A man's got to know his limitations."

 

 

Re: Can DXL functions be aliased?
Mathias Mamsch - Mon Jun 28 11:09:51 EDT 2010

SystemAdmin - Fri Jun 25 18:40:35 EDT 2010

Thanks for the reply.

I tried every syntax variation I could think of. I got those unresolved forward function messages as well. The closest I got was to return a function with the proper return type and signature. That works, but it has to be called like:
 

(functionName())(P1, P2, ...); // ugly

 


I was hoping to use your "New Types" with the struct NewTypeName {}; wiring

but couple it with:

 

 

 

::.(NewTypeName left, aliasedFunction) to do: 
 
instanceOfNewType.memberFunction(P1, P2, ...);
 
 
// as an alternate to:
 
memberFunction(instanceOfNewType, P1, P2, ...);


With my method of returning functions from functions I may be able to do it, but it may require something even uglier to evaluate properly like:

 

 

 

 

(instanceOfNewType.memberFunction())(P1, P2, ...);


This could give the "appearance" of member functions like C++ or Java (or even Object oriented JavaScript).

Without the means aliasing functions in a reasonably easy way such that DXL understands both memberFunction()'s return type AND signature, this will never work.

Like Clint Eastwood as Dirty Harry says "A man's got to know his limitations."

 

 

You can have the objectInstance.memberFunction (parameter, ...) syntax, but you need to do it like the dxl developers did it (for example the properties of the HistoryRecord, etc. are accessed by x.y = z).

But the ugly thing is you need to declare intermediate datatypes (that is exactly the reason why DXL has so many undocumented data types -> to enable the syntax).
 

struct memberFuncType__ {}
 
memberFuncType__ memberFunction (int par1, string par2) {
   // put all the parameters into a DXL object or something
   DxlObject x = new()
   x->"par1" = par1
   x->"par2" = par2
   return (addr_ x) memberFuncType__
}
 
struct Type {}
 
void ::.(Type var, memberFuncType__ var2) {
   DxlObject parameters = (addr_ var2) DxlObject
   int par1 = (parameters->"par1") int
   string par2 = (parameters->"par2") string
   // do the code ...
}
 
Type y 
 
y.memberFunction (1, "Hallo")

 


I did not try the code, but about that should be how DXL does it. Getting to work a property where a.b = ... and ... = a.b works is even uglier (because overriding the ::= operator is generally not recommended :-), but can also be done. )

Hope that helps, Mathias

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

Re: Can DXL functions be aliased?
SystemAdmin - Mon Jun 28 16:05:12 EDT 2010

Mathias Mamsch - Mon Jun 28 11:09:51 EDT 2010

You can have the objectInstance.memberFunction (parameter, ...) syntax, but you need to do it like the dxl developers did it (for example the properties of the HistoryRecord, etc. are accessed by x.y = z).

But the ugly thing is you need to declare intermediate datatypes (that is exactly the reason why DXL has so many undocumented data types -> to enable the syntax).
 

struct memberFuncType__ {}
 
memberFuncType__ memberFunction (int par1, string par2) {
   // put all the parameters into a DXL object or something
   DxlObject x = new()
   x->"par1" = par1
   x->"par2" = par2
   return (addr_ x) memberFuncType__
}
 
struct Type {}
 
void ::.(Type var, memberFuncType__ var2) {
   DxlObject parameters = (addr_ var2) DxlObject
   int par1 = (parameters->"par1") int
   string par2 = (parameters->"par2") string
   // do the code ...
}
 
Type y 
 
y.memberFunction (1, "Hallo")

 


I did not try the code, but about that should be how DXL does it. Getting to work a property where a.b = ... and ... = a.b works is even uglier (because overriding the ::= operator is generally not recommended :-), but can also be done. )

Hope that helps, Mathias

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS

 

Thanks for your reply. What you've shown looks like it will work.

Prior to your reply I was putting the final touches on an experiment that also SEEMS to work with my limited experiment set.

The basic approach is (this is what I think is going on anyway):

[A] Define user-types and member functions you care to expose in the <TYPE>.<MEMBER_FUNCTION>() syntax.
[B] For each unique signature, create a function proxy structure. When a call is made to the function proxy, the desired usage variation will be popped off a user-stack and the appropriate actual function will be called.
[C] In the operator override maps (::.), P1 (the unique type to left of the dot '.') will be pushed on a user stack. The rest of the mess (p2) will be returned as one of those weird _m variables. We are not doing anything with P2 at this time anyway. We are only recording P1 so the behavior of P2 can be altered as needed.

I tried some standalone calls and it appears to work as expected. Nested calls appear to evaluate in the proper order due to use of a user-stack.
 

StringStackType stringStack = (null);
 
//--- Function Proxy ---
string upper(string str) {
    string retVal = "undefined function";
    // Retrieve the variant the use said to call
    string whatToDo = StringStack_pop(stringStack);
    print("In upper, whatToDo = " (whatToDo) "\n");
    //--------------------------------------------------------------------------
    // Here we could have N functions with the same signature.
    // Based on the popped variation (pushed by the ::. function), we resolve 
    // which real function the user wanted to call.
    //--------------------------------------------------------------------------
    DxlObject dummyDxlObject = new();
    if (whatToDo == "Super") {
        retVal = Super_upper(Super_castAsSuper(dummyDxlObject), str);
    } else if (whatToDo == "NumberTranslator") {
        retVal = NumberTranslator_upper(NumberTranslator_castAsNumberTranslator(dummyDxlObject), str);
    } else if (whatToDo == "NumberTranslator2") {
        retVal = NumberTranslator2_upper(NumberTranslator2_castAsNumberTranslator2(dummyDxlObject), str);
    }
    delete dummyDxlObject;
    return retVal;
}
 
 
//*** Maps ****************
_m ::.(Super p1, _m p2) {
    // Push the variant to call later
    StringStack_push(stringStack, "Super");
    return p2
}        
_m ::.(NumberTranslator p1, _m p2) {
    // Push the variant to call later
    StringStack_push(stringStack, "NumberTranslator");
    return p2
 
}        
_m ::.(NumberTranslator2 p1, _m p2) {
    // Push the variant to call later
    StringStack_push(stringStack, "NumberTranslator2");
    return p2
}        
 
 
void myTests() {
    stringStack = StringStack_construct();
    // Super is a collection of DXL functions that
    // have a signature clash with user-type functions
    // with the same signature
    Super super = Super_construct();
    NumberTranslator nt = NumberTranslator_construct();
    NumberTranslator2 nt2 = NumberTranslator2_construct();
    string result;
    string myString = "aaa";
 
    result = super.upper(nt2.upper(myString));
    print("super.upper(nt2.upper(myString)) = " result "\n");
    result = nt.upper(myString);
    print("nt.upper(myString) = " result "\n");
    result = nt2.upper(myString);
    print("nt2.upper(myString) = " result "\n");
    StringStack_destruct(stringStack);
    
}
 
myTests();

 


<OUTPUT>
StringStack.DEBUG.Pushed Super as item 1
StringStack.DEBUG.Pushed NumberTranslator2 as item 2
StringStack.DEBUG.Popped NumberTranslator2 at 2
In upper, whatToDo = NumberTranslator2
StringStack.DEBUG.Popped Super at 1
In upper, whatToDo = Super
super.upper(nt2.upper(myString)) = NUMBERTRANSLATOR2(AAA)
StringStack.DEBUG.Pushed NumberTranslator as item 1
StringStack.DEBUG.Popped NumberTranslator at 1
In upper, whatToDo = NumberTranslator
nt.upper(myString) = NumberTranslator(aaa)
StringStack.DEBUG.Pushed NumberTranslator2 as item 1
StringStack.DEBUG.Popped NumberTranslator2 at 1
In upper, whatToDo = NumberTranslator2
nt2.upper(myString) = NumberTranslator2(aaa)

</OUTPUT>
This isn't fully baked but rather a conceptual example I'm throwing out there for comment.

QUESTION:
Can anyone remark on what is really going on behind the scenes with the _m stuff below?:

 

 

_m ::.(NumberTranslator p1, _m p2) {
    // Push the variant to call later
    StringStack_push(stringStack, "NumberTranslator");
    return p2

 

Re: Can DXL functions be aliased?
Mathias Mamsch - Tue Jun 29 19:03:35 EDT 2010

SystemAdmin - Mon Jun 28 16:05:12 EDT 2010

Thanks for your reply. What you've shown looks like it will work.

Prior to your reply I was putting the final touches on an experiment that also SEEMS to work with my limited experiment set.

The basic approach is (this is what I think is going on anyway):

[A] Define user-types and member functions you care to expose in the <TYPE>.<MEMBER_FUNCTION>() syntax.
[B] For each unique signature, create a function proxy structure. When a call is made to the function proxy, the desired usage variation will be popped off a user-stack and the appropriate actual function will be called.
[C] In the operator override maps (::.), P1 (the unique type to left of the dot '.') will be pushed on a user stack. The rest of the mess (p2) will be returned as one of those weird _m variables. We are not doing anything with P2 at this time anyway. We are only recording P1 so the behavior of P2 can be altered as needed.

I tried some standalone calls and it appears to work as expected. Nested calls appear to evaluate in the proper order due to use of a user-stack.
 

StringStackType stringStack = (null);
 
//--- Function Proxy ---
string upper(string str) {
    string retVal = "undefined function";
    // Retrieve the variant the use said to call
    string whatToDo = StringStack_pop(stringStack);
    print("In upper, whatToDo = " (whatToDo) "\n");
    //--------------------------------------------------------------------------
    // Here we could have N functions with the same signature.
    // Based on the popped variation (pushed by the ::. function), we resolve 
    // which real function the user wanted to call.
    //--------------------------------------------------------------------------
    DxlObject dummyDxlObject = new();
    if (whatToDo == "Super") {
        retVal = Super_upper(Super_castAsSuper(dummyDxlObject), str);
    } else if (whatToDo == "NumberTranslator") {
        retVal = NumberTranslator_upper(NumberTranslator_castAsNumberTranslator(dummyDxlObject), str);
    } else if (whatToDo == "NumberTranslator2") {
        retVal = NumberTranslator2_upper(NumberTranslator2_castAsNumberTranslator2(dummyDxlObject), str);
    }
    delete dummyDxlObject;
    return retVal;
}
 
 
//*** Maps ****************
_m ::.(Super p1, _m p2) {
    // Push the variant to call later
    StringStack_push(stringStack, "Super");
    return p2
}        
_m ::.(NumberTranslator p1, _m p2) {
    // Push the variant to call later
    StringStack_push(stringStack, "NumberTranslator");
    return p2
 
}        
_m ::.(NumberTranslator2 p1, _m p2) {
    // Push the variant to call later
    StringStack_push(stringStack, "NumberTranslator2");
    return p2
}        
 
 
void myTests() {
    stringStack = StringStack_construct();
    // Super is a collection of DXL functions that
    // have a signature clash with user-type functions
    // with the same signature
    Super super = Super_construct();
    NumberTranslator nt = NumberTranslator_construct();
    NumberTranslator2 nt2 = NumberTranslator2_construct();
    string result;
    string myString = "aaa";
 
    result = super.upper(nt2.upper(myString));
    print("super.upper(nt2.upper(myString)) = " result "\n");
    result = nt.upper(myString);
    print("nt.upper(myString) = " result "\n");
    result = nt2.upper(myString);
    print("nt2.upper(myString) = " result "\n");
    StringStack_destruct(stringStack);
    
}
 
myTests();

 


<OUTPUT>
StringStack.DEBUG.Pushed Super as item 1
StringStack.DEBUG.Pushed NumberTranslator2 as item 2
StringStack.DEBUG.Popped NumberTranslator2 at 2
In upper, whatToDo = NumberTranslator2
StringStack.DEBUG.Popped Super at 1
In upper, whatToDo = Super
super.upper(nt2.upper(myString)) = NUMBERTRANSLATOR2(AAA)
StringStack.DEBUG.Pushed NumberTranslator as item 1
StringStack.DEBUG.Popped NumberTranslator at 1
In upper, whatToDo = NumberTranslator
nt.upper(myString) = NumberTranslator(aaa)
StringStack.DEBUG.Pushed NumberTranslator2 as item 1
StringStack.DEBUG.Popped NumberTranslator2 at 1
In upper, whatToDo = NumberTranslator2
nt2.upper(myString) = NumberTranslator2(aaa)

</OUTPUT>
This isn't fully baked but rather a conceptual example I'm throwing out there for comment.

QUESTION:
Can anyone remark on what is really going on behind the scenes with the _m stuff below?:

 

 

_m ::.(NumberTranslator p1, _m p2) {
    // Push the variant to call later
    StringStack_push(stringStack, "NumberTranslator");
    return p2

 

Awesome find ... It is really hard to believe that this works, but it does.
 

struct MyType {}
 
void test(int a) {
  print "in test " a "\n"
}
 
_m ::.(MyType x, _m p2) {
   print "In operator\n"
   return p2
}
 
MyType v = null
 
v.test 4

 


This example outputs "In operator" then "In test" meaning the . operator is called first. But if you replace v.test 4 by v.test "" then the compiler will tell you "incorrect arguments for function test."

This means you fooled the DXL parser two times. Normally DXL should have called test 4 first (right associativity!) but obviously the _m parameters and _m return types make it forget about right associativity. The question is, what happens with the 4? Let us see this by turning on code printing:

 

 

 

(CC: (
1.i0(   BlockI 0 3 0)
1.i4(   IntAss, 1.i9, 1.i16, 1.i22)         // translates to 'obj = currentObj()
1.i9( PushVarA, obj, 0,  0)^
1.i14(      Nop)
1.i15(      Nop)
1.i16(CurrentObj)^
1.i20(      Nop)
1.i21(      Nop)
1.i22(      Pop)
1.i25(   IntAss, 1.i30, 1.i37, 1.i43)
1.i30( PushVarA, attrDXLName, 0,  1)^               // translates to: attrDXLName = AttrDXLNameGet
1.i35(      Nop)
1.i36(      Nop)
1.i37(AttrDXLNameGet)^
1.i41(      Nop)
1.i42(      Nop)
1.i43(      Pop)
1.i46(     Jump, 1.i136)                            // jmp to 136 -->
1.i50(   BlockI 0 0 1)                              // start of function test 
1.i54(StringPrint, 1.i58, 1.i89)
1.i58(StringConcat, 1.i63, 1.i70, )
1.i63(     Push da[4(in test )])^
1.i68(      Nop)
1.i69(      Nop)
1.i70(StringIntConcat, 1.i75, 1.i82, )
1.i75( PushVarC, a, 1,  0)^
1.i80(      Nop)
1.i81(      Nop)
1.i82(     Push da[13])^
1.i87(      Nop)
1.i88(      Nop)
1.i89(  ReUnAss #Q#(192))
1.i93(   BlockE 0 0 1)
1.i97(      Ret)
1.i100(   BlockI 1 0 1)                                 // start of function ::. ()
1.i104(StringPrint, 1.i108, 1.i115)
1.i108(     Push da[20(In operator
)])^
1.i113(      Nop)
1.i114(      Nop)
1.i115(     RetV)
1.i118( PushVarC, p2, 1,  1)^
1.i123(      Nop)
1.i124(      Nop)
1.i125(  ReUnAss #Q##(216))
1.i129(   BlockE 1 0 1)
1.i133(      Ret)
1.i136(   IntAss, 1.i141, 1.i148, 1.i154)         // start of main program 
1.i141( PushVarA, v, 0,  2)^                      // translates to v = null
1.i146(      Nop)
1.i147(      Nop)
1.i148(  NullPtr)^
1.i152(      Nop)
1.i153(      Nop)
1.i154(      Pop)
1.i157(   CallFn, 1.i162, 1.i185, 1.i192)   // This means execute 162, then execute 185, then execute the result of 162. 
1.i162(CallFnRes, 1.i167, 1.i174, )       // Instead of pushing a function address here 'push xy', 
1.i167(     Push #Q##(216))^                // we have the function call to the . operator here!
1.i172(      Nop)                           // Thats weird!
1.i173(      Nop)
1.i174( PushVarC, v, 0,  2)
1.i178(     Push #Q#(192))^
1.i183(      Nop)
1.i184(      Nop)
1.i185(     Push 4)^
1.i190(      Nop)
1.i191(      Nop)
1.i192(      Nop)
1.i195(  ReUnAss #Q#(168))
1.i199(  ReUnAss #Q#(96))
1.i203(   BlockE 0 3 0)^
1.i208(      Nop)
1.i209(      Nop)
)
)



When you read carefully you will see, that the DXL interpreter will really evaluate (v.test) first and then push the 4 onto the stack and call the resulting function with the parameter 4. This is exactly what you wanted. Really nice find.

The thing is that the member functions of the type still would need the object instance (parameter p1 in your code), which you ignore completly in the ::. operator. To pass it to the member function you would need to put the instance handle somewhere, so the approach really has no advantages over the intermediate type approach. I really wonder where this could be useful - I feel this could come in handy some time.

Regards, Mathias



 

 

 

 


Mathias Mamsch, IT-QBase GmbH, Consultant for Requirement Engineering and D00RS